#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define LOGIN_SUCCESS 1
#define LOGIN_FAILURE 0

void printf_client_info(struct sockaddr_in *addr, char * buf) {
    printf("================\n");
    // 网络字节序转IP
    printf("user IP: %s\n", inet_ntoa(addr->sin_addr));
    // 网络字节序端口转换 
    printf("user port : %d\n", ntohs(addr->sin_port));
    // 发送数据
    printf("user data: %s\n", buf);

}

// 接收数据

void recv_data(int sockfd) {
    char buf[1024] = {0};
    struct sockaddr_in my_addr;
    socklen_t len = sizeof(my_addr);
    int n = 0;

    while(1) {
        memset(buf, 0, sizeof(buf));
        n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&my_addr, &len);
        if (n < 0) {
            perror("recvfrom fail");
            exit(EXIT_FAILURE);
        }
        // 接收的ip port  以及数据
        printf("recv from ip = %s \n", inet_addr(my_addr.sin_addr.s_addr));
        printf("recv from port = %d \n", ntohs(my_addr.sin_port));
        printf("recv data = %s \n", buf);
        if (strncmp(buf, "exit", 4)) {
            break;
        }
    }
    close(sockfd);
    // 退出的时候结束子进程
    exit(EXIT_SUCCESS);

    return;
}

// 初始化socket
int init_socket(const char *ip, const char *port) {
    struct sockaddr_in my_addr;  //sockaddr_in 解决了sockaddr的缺陷， 
    socklen_t len = sizeof(my_addr);
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    memset(&my_addr, 0, len);
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(atoi(port));
    my_addr.sin_addr.s_addr = inet_addr(ip);
    // bind
    if (bind(sockfd, (struct sockaddr *)&my_addr, len) == -1) {
        perror("bind fail");
        return -1;
    }
    return sockfd;

}

int user_login(const char *ip, const char *port) {
    int sockfd = init_socket(ip, port);
    int new_sockfd;
    // 接收消息
    int n = 0;
    char buf[20] = {0};
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);
    int login_flag = 0;
    // 父进程负责验证密钥信息， 子进程负责与用户进行数据交互
    while(1) {
        memset(buf, 0, sizeof(buf));
        n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *) &client_addr, &len);
        if (n < 0) {
            perror("recvfrom fail");
            exit(EXIT_FAILURE);
        }
        // 打印密钥
        printf("key = %s\n", buf);
        login_flag = strncmp(buf, "root", 4) ? LOGIN_SUCCESS : LOGIN_FAILURE;
        if (login_flag == LOGIN_SUCCESS) {
            if(fork() == 0) {
                printf("child process\n");
                //子进程会copy父进程的文件描述符， 
                close(sockfd);
                // 绑定0号端口， 系统会随机分配一个可用的端口
                new_sockfd = init_socket(ip, "0");

                // 新的子进程端口与客户端交互， 回复登陆成功
                sendto(new_sockfd, &login_flag, sizeof(login_flag), 0, (struct sockaddr *) &client_addr, len);
                break;

            }
        } else {
            // 密钥错误， 服务端返回给客户端, 回复login_flag
            sendto(sockfd, &login_flag, sizeof(login_flag), 0, (struct sockaddr *) &client_addr, len);
        }
        printf_client_info(&client_addr, buf);
    }
    return new_sockfd;
   
}


int main(int argc, char const *argv[])
{
    /* code */
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    //接收用户密钥
    int sockfd = user_login(argv[1], argv[2]);

    // 接收数据
    recv_data(sockfd);

    close(sockfd);
   
    return 0;
}
